/**
 * Trabalho 2 
 * OpenMP 2 - Texto shakespe.txt 
 *
 * GRUPO 9 : Alfredo L. Porfirio
 *           Diego Gomes da Silva
 *           Marcos V. S. Ferreira
 *
 * Compile: g++ -o omp2 omp2.cpp -fopenmp
 */

// Bibliotecas
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include <omp.h>

// Ambiente
using namespace std;

// contadores de palavras e frases palindromas
int n_pali_word = 0;
int n_pali_phrase = 0;

// passa a string para caixa alta
string toUpperCase(string s) {
  for (int i = 0; i < s.size(); i++)
    s[i]=toupper(s[i]);
  return s;
}

// Limpa a string de apontuacao(menos pontos, interrogacoes e exclamacoes)
void remove_pontuacion(string& in)
{
    for(int i=0; i <in.length(); i++)
    {
        if( in[i] != '.' && in[i] != '!' && in[i] != '?' && ispunct(in[i]))
          in[i]= ' ';
    }
    return;
}


// verifica se um string é palindromo, utilizando-se de interators
bool palindrome(string s) {
  if( equal(s.begin(), s.begin() + s.size()/2, s.rbegin()) )
      return true;
  else
      return false;
}

// remove o ultimo caracter da string, caso seja uma pontuacao
string pop_back(string& in) {
  string out = in;
  if( ispunct(in[in.size()-1]) ) {
    out = in.substr(0, in.size()-1);
  }
  return out;
}

// mostra o ultimo caracter da string
char back(string& in) {
  return in[in.size()-1];
}

// Pega a linha do aquivo e quebra em palavras e junta novamentes em uma frase
void token(string& in) {
    istringstream iss(in);
    string phrase;

    do
    {
        string sub;
        string aux;
        iss >> aux; // pega a proxima string 
        
        aux = toUpperCase(aux);
        sub = pop_back(aux);
        if( sub.size() >2 && palindrome(sub) )  // Verifica se a palavra é palindromo 
            n_pali_word++;

        if( back(aux) != '.' && back(aux) != '!' && back(aux) != '?' && iss) {
          phrase += sub; // junta numa frase
        }
        else {
          
          phrase += sub; // junta em uma frase e jah verifica

          if(phrase.size() > 2 && palindrome(phrase) ) // verifica se a frase eh palindromo 
            n_pali_phrase++; 
  
          phrase.clear();
        }
    } while (iss);
}
#define MICRO_PER_SECOND 1000000 


int main() {
    struct timeval start_time;
    struct timeval stop_time;
    float  time_texto;

    gettimeofday( &start_time, NULL );
    
    
    
    ifstream fin ("shakespe.txt"); // ifstream = padrão ios:in
    if (fin.is_open())
    {
      #pragma omp parallel shared(fin) reduction(+:n_pali_word,n_pali_phrase)
      {
          int local_pali_word = 0;
          int local_pali_phrase = 0;
          while ( !fin.eof() ) //enquanto end of file for false continua
          {
              string line;
              #pragma omp critical 
              {        
                getline(fin, line, '\r'); // como foi aberto em modo texto(padrão)
              }
              remove_pontuacion(line);


              istringstream iss(line);
              string phrase;
              do
              {
                string sub;
                string aux;
                iss >> aux;
                
                aux = toUpperCase(aux);
                sub = pop_back(aux);
                if( sub.size() >2 && palindrome(sub) ){  // Verifica se a palavra é palindromo 
                    n_pali_word++;local_pali_word++;
                }
                if( back(aux) != '.' && back(aux) != '!' && back(aux) != '?' && iss) {
                  phrase += sub;
                }
                else {
                  
                  phrase += sub;

                  //cout << phrase << endl;
                  if(phrase.size() > 2 && palindrome(phrase) ){ // verifica se a frase eh palindromo 
                    n_pali_phrase++;local_pali_phrase++; 
                  } 
                  phrase.clear();
                }
              } while (iss);

              line.clear();
          }
          cout << "numero de palavras palindromas por thread: " << omp_get_thread_num() <<": " << local_pali_word << endl;
          cout << "numero de frases palindromas por thread: " << omp_get_thread_num() <<": " << local_pali_phrase << endl;
        }
      fin.close();
    }
    else cout << "nao foi possivel abrir o arquivo.." << endl; 

    gettimeofday( &stop_time, NULL );
    time_texto = (float)(stop_time.tv_sec - start_time.tv_sec);
    time_texto += (stop_time.tv_usec - start_time.tv_usec)/(float)MICRO_PER_SECOND; 

    
    cout << "numero de palavras palindromas: " << n_pali_word << endl;
    cout << "numero de frases palindromas: " << n_pali_phrase << endl;
    cout << "Tempo de Processamento do Texto: " << time_texto << " s" << endl;

    return 0;
}